package ar.edu.utn.frba.disenio.model.tests.entrega3;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import junit.framework.Assert;
import org.junit.Before;
import org.junit.Test;
import ar.edu.utn.frba.disenio.fechas.Fecha;
import ar.edu.utn.frba.disenio.fechas.Formato;
import ar.edu.utn.frba.disenio.model.*;
import ar.edu.utn.frba.disenio.model.exceptions.*;

public class ModelTest {
	
	Lugar buenosAires = new Lugar("BSAS", "_BS");
	Lugar cordoba = new Lugar("COR","COR");
	Lugar losAngeles = new Lugar("LA", "SLA");
	Formato formato = new Formato("dd/MM/yyyy");
	List<Aerolinea> aerolineas = new ArrayList<Aerolinea>();
	UsuarioEstandar unUsuarioEstandar = new UsuarioEstandar("34342234");
	UsuarioEstandar otroUsuarioEstandar = new UsuarioEstandar("36688694");
	UsuarioFree unUsuarioFree = new UsuarioFree("34324214");
	Asiento unAsientoDeOceanic = new Asiento("MOCK099", 3, new Turista(), new Ventana(), new BigDecimal("1000"), new Oceanic(OceanicMOCK.getInstance()));
	Asiento unAsientoCarisimo = new Asiento("MOCK099", 3, new Turista(), new Ventana(), new BigDecimal("1000000"), new Oceanic(OceanicMOCK.getInstance()));
	UsuarioEstandar usuarioVIP = new UsuarioEstandar("111");
	
	@Before
	public void setUp(){
		aerolineas.add(new Oceanic(new OceanicMOCK()));
		unAsientoCarisimo.comprar(usuarioVIP);
		Vuelo.resetVuelosPopulares();
	}
		
	@Test
	public void buscaVueloYDevuelveLaCantidadCorrecta(){
		Busqueda unaBusqueda = new Busqueda(buenosAires, losAngeles, new Fecha(formato,"30/11/2012"), null);
		Assert.assertEquals(1, unaBusqueda.buscarVuelos(aerolineas, unUsuarioEstandar).size());
	}
	
	@Test
	public void buscaVueloConPreciosDeterminados(){
		/*Setteo Filtros*/
		List<Filtro> filtros = new ArrayList<Filtro>();
		FiltroPorPrecio filtroPrecio = new FiltroPorPrecio(new BigDecimal(1000), new BigDecimal(3000));
		filtros.add(filtroPrecio);
		/*Busqueda*/
		Busqueda unaBusqueda = new Busqueda(buenosAires, losAngeles, new Fecha(formato, "30/11/2012"), filtros);
		unaBusqueda.buscarVuelos(aerolineas, usuarioVIP);
		Assert.assertEquals(2, unaBusqueda.getTodosLosAsientos().size());
	}
	
	@Test
	public void buscaVueloConAsientosTurista(){
		/*Setteo Filtros*/
		List<Filtro> filtros = new ArrayList<Filtro>();
		List<Categoria> categorias = new ArrayList<Categoria>();
		categorias.add(new Turista());
		FiltroPorCategorias filtroTurista = new FiltroPorCategorias(categorias);
		filtros.add(filtroTurista);
		/*Busqueda*/
		Busqueda unaBusqueda = new Busqueda(buenosAires, losAngeles, new Fecha(formato, "30/11/2012"), filtros);
		unaBusqueda.buscarVuelos(aerolineas, unUsuarioEstandar);
		Assert.assertEquals(1, unaBusqueda.getTodosLosAsientos().size());
	}
	
	@Test
	public void buscaVueloConAsientosEnVentana(){
		/*Setteo Filtros*/
		List<Filtro> filtros = new ArrayList<Filtro>();
		List<Ubicacion> ubicaciones = new ArrayList<Ubicacion>();
		ubicaciones.add(new Ventana());
		FiltroPorUbicaciones filtroVentana = new FiltroPorUbicaciones(ubicaciones);
		filtros.add(filtroVentana);
		/*Busqueda*/
		Busqueda unaBusqueda = new Busqueda(buenosAires, losAngeles, new Fecha(formato, "30/11/2012"), filtros);
		unaBusqueda.buscarVuelos(aerolineas, usuarioVIP);
		Assert.assertEquals(4, unaBusqueda.getTodosLosAsientos().size());
		for (Asiento unAsiento : unaBusqueda.getTodosLosAsientos()){
			Assert.assertTrue(unAsiento.getUbicacion().esVentana());	
		}
	}
	
	@Test
	public void usuarioVIPBuscaAsientosYRecibeTodos(){
		Busqueda unaBusqueda = new Busqueda(buenosAires, losAngeles, new Fecha(formato,"30/11/2012"), null);
		unaBusqueda.buscarVuelos(aerolineas, usuarioVIP);
		Assert.assertEquals(8, unaBusqueda.getTodosLosAsientos().size());
	}
	
	@Test
	public void seRealizaUnaSobrereservaYSeCompruebaQueSeGrabeElUsuario(){
		Usuario unUsuario = new UsuarioEstandar("34324231");
		Usuario otroUsuario = new UsuarioEstandar("36699832");
		unAsientoDeOceanic.reservar(unUsuario);
		unAsientoDeOceanic.reservar(otroUsuario);
		Assert.assertEquals(1, unAsientoDeOceanic.getSobrereservas().size());
		Assert.assertTrue(unAsientoDeOceanic.getSobrereservas().get(0).esElMismoUsuario(otroUsuario));
	}
	
	@Test
	public void seRealizaUnaReservaYSeCompruebaQueSeGrabeElUsuario(){
		unAsientoDeOceanic.reservar(unUsuarioEstandar);
		Assert.assertTrue(unAsientoDeOceanic.getReserva().esElMismoUsuario(unUsuarioEstandar));
	}

	@Test
	public void seCompruebanCambiosDeEstadoEnUnAsiento(){
		Assert.assertTrue(unAsientoDeOceanic.getEstado().estaDisponible());
		unAsientoDeOceanic.reservar(unUsuarioEstandar);
		Assert.assertTrue(unAsientoDeOceanic.getEstado().estaReservado());
		unAsientoDeOceanic.comprar(unUsuarioEstandar);
		Assert.assertTrue(unAsientoDeOceanic.getEstado().estaComprado());
	}
	
	@Test
	public void seCompruebaQueNoHayanSobreReservasDespuesDeComprarUnAsiento(){
		unAsientoDeOceanic.reservar(unUsuarioEstandar);
		unAsientoDeOceanic.reservar(otroUsuarioEstandar);
		Assert.assertEquals(1, unAsientoDeOceanic.getSobrereservas().size());
		unAsientoDeOceanic.comprar(unUsuarioEstandar);
		Assert.assertEquals(0, unAsientoDeOceanic.getSobrereservas().size());
	}
	
	@Test (expected = YaLoReservoElMismoException.class)
	public void unUsuarioIntentaReservarVariasVecesYNoPuede(){
		unAsientoDeOceanic.reservar(unUsuarioEstandar);
		unAsientoDeOceanic.reservar(unUsuarioEstandar);
	}
	
	@Test (expected = YaLoReservoElMismoException.class)
	public void unUsuarioIntentaSobrereservarVariasVecesYNoPuede(){
		unAsientoDeOceanic.reservar(unUsuarioEstandar);
		unAsientoDeOceanic.reservar(otroUsuarioEstandar);
		unAsientoDeOceanic.reservar(otroUsuarioEstandar);
	}
	
	@Test (expected = ReservarNoDisponibleParaUsuarioException.class)
	public void usuarioFreeIntentaReservarYNoPuede(){
		unAsientoDeOceanic.reservar(unUsuarioFree);
	}
	
	@Test
	public void seCompruebaQueLosPreciosDeLosAsientosDeUnaBusquedaEstenOrdenados(){
		Busqueda unaBusqueda = new Busqueda(buenosAires, losAngeles, new Fecha(formato,"30/11/2012"), null);
		unaBusqueda.buscarVuelos(aerolineas, usuarioVIP);
		List<Asiento> asientosOrdenados = unaBusqueda.getAsientosOrdenados(new OrdenPrecioCreciente());
		Asiento anteriorAsiento = asientosOrdenados.get(0);
		for (Asiento unAsiento : asientosOrdenados){
			Assert.assertTrue(anteriorAsiento.getPrecioBase().compareTo(unAsiento.getPrecioBase()) <= 0);
			anteriorAsiento = unAsiento;
		}
	}
	
	@Test
	public void seCompruebaQueLosVuelosDeLosAsientosDeUnaBusquedaSeOrdenenPorTiempoDeVuelo(){
		Busqueda unaBusqueda = new Busqueda(buenosAires, losAngeles, new Fecha(formato,"30/11/2012"), null);
		unaBusqueda.buscarVuelos(aerolineas, usuarioVIP);
		List<VueloDirecto> vuelosOrdenados = unaBusqueda.getVuelosOrdenados(new OrdenPorTiempoVueloCreciente());
		VueloDirecto vueloAnterior = vuelosOrdenados.get(0);
		for (VueloDirecto unVuelo : vuelosOrdenados){
			Assert.assertTrue(vueloAnterior.getTiempoDeVuelo().compareTo(unVuelo.getTiempoDeVuelo()) <= 0);
			vueloAnterior = unVuelo;
		}
	}
	
	@Test
	public void testUnaEscala(){
		Lugar mexico = new Lugar("", "MEX");
		Busqueda unaBusqueda = new Busqueda(buenosAires, mexico, new Fecha(formato,"30/11/2012"), null);
		List<Aerolinea> aerolineas = new ArrayList<Aerolinea>();
		aerolineas.add(new Oceanic(new OceanicUnaEscalaMOCK()));
		Assert.assertEquals(1, (int)unaBusqueda.buscarVuelosEscalados(aerolineas, usuarioVIP).get(0).cantidadEscalas());
		Assert.assertEquals(1, unaBusqueda.buscarVuelosEscalados(aerolineas, usuarioVIP).size());
		
	}
	
	@Test
	public void testDosEscalas() {
		Lugar mexico = new Lugar("", "MEX");
		Busqueda unaBusqueda = new Busqueda(buenosAires, mexico, new Fecha(formato,"30/11/2012"), null);
		List<Aerolinea> aerolineas = new ArrayList<Aerolinea>();
		aerolineas.add(new Oceanic(new OceanicDosEscalasMOCK()));
		Assert.assertEquals(2, (int)unaBusqueda.buscarVuelosEscalados(aerolineas, usuarioVIP).get(0).cantidadEscalas());
		Assert.assertEquals(2, unaBusqueda.buscarVuelosEscalados(aerolineas, usuarioVIP).size());
	}
	
	@Test (expected = NoHayVuelosConEscalaDisponibles.class)
	public void testConMasDeDosEscalasDebeDarVacio() {
		Lugar brasil = new Lugar("", "BRS");
		Busqueda unaBusqueda = new Busqueda(buenosAires, brasil, new Fecha(formato,"30/11/2012"), null);
		List<Aerolinea> aerolineas = new ArrayList<Aerolinea>();
		aerolineas.add(new Oceanic(new OceanicTresEscalasMOCK()));
		Assert.assertEquals(0, unaBusqueda.buscarVuelosEscalados(aerolineas, usuarioVIP).size());
	}
	
	@Test
	public void compraAsientosDeUnVueloYChequeaPopularidad(){
		String codigoVuelo = "MOCK098";
		Assert.assertEquals((Integer)0, (Integer)Vuelo.getPopularidad(codigoVuelo));
		Asiento asiento1 = new Asiento(codigoVuelo, 1, new Turista(), new Ventana(), new BigDecimal("1000"), new Oceanic(OceanicMOCK.getInstance()));
		Asiento asiento2 = new Asiento(codigoVuelo, 2, new Turista(), new Ventana(), new BigDecimal("1000"), new Oceanic(OceanicMOCK.getInstance()));
		Asiento asiento3 = new Asiento(codigoVuelo, 3, new Turista(), new Ventana(), new BigDecimal("1000"), new Oceanic(OceanicMOCK.getInstance()));
		asiento1.comprar(usuarioVIP);
		asiento2.comprar(usuarioVIP);
		asiento3.comprar(usuarioVIP);
		Assert.assertEquals((Integer)3, (Integer)Vuelo.getPopularidad(codigoVuelo));
	}
	
	@Test
	public void seCompranAsientosDeDistintosVuelosYSeOrdenaPorPopularidad(){
		Busqueda unaBusqueda = new Busqueda(buenosAires, losAngeles, new Fecha(formato,"30/11/2012"), null);
		List<Aerolinea> aerolineas = new ArrayList<Aerolinea>();
		aerolineas.add(new Oceanic(new OceanicVuelosMOCK()));
		Asiento asiento1 = new Asiento("OC99", 1, new Turista(), new Ventana(), new BigDecimal("1000"), new Oceanic(OceanicMOCK.getInstance()));
		Asiento asiento2 = new Asiento("OC99", 1, new Turista(), new Ventana(), new BigDecimal("1000"), new Oceanic(OceanicMOCK.getInstance()));
		Asiento asiento3 = new Asiento("OC99", 1, new Turista(), new Ventana(), new BigDecimal("1000"), new Oceanic(OceanicMOCK.getInstance()));
		Asiento asiento4 = new Asiento("OC98", 1, new Turista(), new Ventana(), new BigDecimal("1000"), new Oceanic(OceanicMOCK.getInstance()));
		Asiento asiento5 = new Asiento("OC98", 1, new Turista(), new Ventana(), new BigDecimal("1000"), new Oceanic(OceanicMOCK.getInstance()));
		unaBusqueda.buscarVuelos(aerolineas, usuarioVIP);
		asiento1.comprar(usuarioVIP);
		asiento2.comprar(usuarioVIP);
		asiento3.comprar(usuarioVIP);
		asiento4.comprar(usuarioVIP);
		asiento5.comprar(usuarioVIP);
		List<VueloDirecto> vuelosOrdenados = unaBusqueda.getVuelosOrdenados(new OrdenPorPopularidad());
		VueloDirecto vueloAnterior = vuelosOrdenados.get(0);
		for(VueloDirecto unVuelo : vuelosOrdenados){
			Assert.assertTrue(vueloAnterior.getPopularidad()>=unVuelo.getPopularidad());
			vueloAnterior = unVuelo;
		}
	}
	
	@Test
	public void sePideElTiempoDeVueloDeUnVueloYEsCorrecto(){
		Busqueda unaBusqueda = new Busqueda(buenosAires, losAngeles, new Fecha(formato,"30/11/2012"), null);
		List<Aerolinea> aerolineas = new ArrayList<Aerolinea>();
		aerolineas.add(new Oceanic(new OceanicUnaEscalaMOCK()));
		unaBusqueda.buscarVuelos(aerolineas, usuarioVIP);
		Vuelo unVuelo = unaBusqueda.getVuelosEncontrados().get(0);
		Assert.assertTrue(1==unVuelo.getTiempoDeVuelo());
		Vuelo otroVuelo = unaBusqueda.getVuelosEncontrados().get(0);
		Assert.assertTrue(1==otroVuelo.getTiempoDeVuelo());
	}
	
	@Test
	public void sePideElTiempoDeVueloDeUnVueloEscaladoYEsCorrecto() {
		//Hago busquedas para obtener el vuelo escalado
		Lugar mexico = new Lugar("", "MEX");
		Busqueda unaBusqueda = new Busqueda(buenosAires, mexico, new Fecha(formato,"30/11/2012"), null);
		List<Aerolinea> aerolineas = new ArrayList<Aerolinea>();
		aerolineas.add(new Oceanic(new OceanicDosEscalasMOCK()));
		List<VueloEscalado> vuelosEscalados = unaBusqueda.buscarVuelosEscalados(aerolineas, usuarioVIP);
		VueloEscalado unVueloEscalado = vuelosEscalados.get(0);
		//Assert
		Assert.assertTrue(3==unVueloEscalado.getTiempoDeVuelo());
	}
	
}
